package me.zhengjie.tool;

import com.google.common.math.DoubleMath;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class BigDecimalUtil implements Serializable {

    private static final long serialVersionUID = -1527252416630598484L;

    public static final String [] HanDigiStr = new String[]{"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};

    public static final String [] HanDiviStr = new String[]{"","拾","佰","仟","万","拾","佰","仟","亿",
            "拾","佰","仟","万","拾","佰","仟","亿",
            "拾","佰","仟","万","拾","佰","仟" };

    /**
     * 由于Double类型创建时会出现精度丢失的情况，这里使用string转换
     * @param str
     * @return
     */
    public static BigDecimal getBigDecimal(String str){
        if(ValidateUtil.isEmpty(str)){
            throw new IllegalArgumentException("创建BigDecimal时传入参数不正确！");
        }
        return new BigDecimal(str);
    }

    public static BigDecimal nullDispose(BigDecimal bigDecimal){
        if(ValidateUtil.isEmpty(bigDecimal)){
            return BigDecimal.ZERO;
        }
        return bigDecimal;
    }
    public static BigDecimal getBigDecimal(Integer integer){
        if(ValidateUtil.isEmpty(integer)){
            return getBigDecimal("0");
        }
        return getBigDecimal(integer.toString());
    }
    public static BigDecimal getBigDecimal(Float ft){
        if(ValidateUtil.isEmpty(ft)){
            return getBigDecimal("0.0");
        }
        return getBigDecimal(ft.toString());
    }
    public static BigDecimal getBigDecimal(Double db){
        if(ValidateUtil.isEmpty(db)){
            return getBigDecimal("0.00");
        }
        return getBigDecimal(db.toString());
    }

    /**
     * 金额精度处理
     * @param bd 金额
     * @param digit 保留多少位小数
     * @param flag true:截取小数后一位有值就进一位   false:四舍五入
     * @return
     */
    public static BigDecimal moneyPrecision(BigDecimal bd, int digit, boolean flag){
        if(ValidateUtil.isEmpty(bd)) {
            return bd;
        }
        String str = String.valueOf(bd);
        if(str.indexOf('.') < 0) {
            return bd;
        }
        String left = str.split("\\.")[0];
        String right = str.split("\\.")[1];
        if(right.length() == digit) {
            return bd;
        }
        if(right.length() < digit){
            int num = digit - right.length();
            String strZero = "";
            for(int i=0;i<num;i++){
                strZero+="0";
            }
            return new BigDecimal(left + "." + right + strZero);
        }
        String newRight = right.substring(0, digit);
        String numRight = right.substring(digit, digit+1);
        if("0".equals(numRight)) {
            return new BigDecimal(left + "." + newRight);
        }
        int leng = newRight.length();
        String addNum = "";
        for(int i=0;i<leng-1;i++){
            addNum += "0";
        }
        addNum = "0." + addNum + "1";
        if(flag){
            return new BigDecimal(left + "." + newRight).add(new BigDecimal(addNum));
        }
        if(Integer.valueOf(numRight) < 5) {
            addNum = "0";
        }
        return new BigDecimal(left + "." + newRight).add(new BigDecimal(addNum));
    }

    /**
     * 检查数值是否为NULL，为null则抛出异常
     * @author tianqi
     * @see
     * @param b 传入的需要检查的数值
     * @param msg 这个传入数值的名字
     * @throws Exception
     * @datetime 2016年8月9日 下午8:23:27
     * @version 1.0
     */
    public static void checkNull(BigDecimal b, String msg) {
        BaseUtil.checkMsg(msg);
        if(ValidateUtil.isEmpty(b)){
            throw new IllegalArgumentException("没有获取到[" + msg + "]");
        }
    }

    /**
     * 检查数值是否小于0, 小于0则抛出异常
     * @author tianqi
     * @see
     * @param b 传入的需要检查的数值
     * @param msg 这个传入数值的名字
     * @throws Exception
     * @datetime 2016年8月9日 下午8:28:59
     * @version 1.0
     */
    public static void checkIsPositiveNumber(BigDecimal b, String msg) {
        checkNull(b, msg);
        if(0.00 > b.doubleValue()){
            throw new IllegalArgumentException("[" + msg + "]不能为负数");
        }
    }

    /**
     * 判断小数点位数是否超出两位
     * @author tangyj
     * @param num
     * @throws Exception
     * @datetime 2016年8月17日 下午9:30:12
     * @version 1.0
     */
    public static void is2scaleThrows(BigDecimal num) throws Exception{
        checkNull(num, "参数为空");
        boolean i = DoubleMath.isMathematicalInteger(multiply(num, BigDecimal.valueOf(100.00), 6).doubleValue());
        if(!i) {
            throw new IllegalArgumentException(num.toString()+"小数点位数超出两位!");
        }
    }

    /**
     * 判断小数点位数是否超出两位
     * @author tangyj
     * @param num
     * @throws Exception
     * @datetime 2016年8月17日 下午9:30:12
     * @version 1.0
     */
    public static Boolean is2scaleReturn(BigDecimal num) throws Exception{
        checkNull(num, "参数为空");
        return DoubleMath.isMathematicalInteger(multiply(num, BigDecimal.valueOf(100.00), 6).doubleValue());

    }

    /**
     * 检查传入数值的边界，不能小于等于多少，不能大于等于多少
     * @author tianqi
     * @see
     * @param num 数值
     * @param bottom 下边界
     * @param top 上边界
     * @param name 数值名称
     * @throws Exception
     * @datetime 2016年8月9日 下午8:50:49
     * @version 1.0
     */
    public static void checkRange(BigDecimal num, BigDecimal bottom, BigDecimal top, String name){
        checkNull(num, "需要判定范围的数值");
        checkNull(bottom, "下边界");
        checkNull(top, "上边界");
        if(num.doubleValue() >= bottom.doubleValue() && num.doubleValue() <= top.doubleValue()){
            return;
        }
        if(num.doubleValue() >= top.doubleValue() && num.doubleValue() <= bottom.doubleValue()){
            return;
        }
        throw new IllegalArgumentException(name + "[" + num.toString() + "]没有在 " + bottom.toString() + " 与 " + top.toString() + " 之间");
    }

    /**
     * 加法
     * @author tianqi
     * @see
     * @param radix 加数1
     * @param ratio 加数2
     * @param scale 四舍五入保留小数
     * @return
     * @throws Exception
     * @datetime 2016年8月9日 下午8:42:53
     * @version 1.0
     */
    public static BigDecimal add(BigDecimal radix, BigDecimal ratio, Integer scale){
        //检查是否为null
        checkNull(radix, "加数1");
        checkNull(ratio, "加数2");
        BaseUtil.checkNull(scale, "四舍五入保留位");
        //检查是否小于0
//		checkIsPositiveNumber(radix, "加数1");
//		checkIsPositiveNumber(ratio, "加数2");
        //检查四舍五入是否在0-16之间
        BaseUtil.checkRange(scale, 0, 16, "四舍五入保留小数位");
        return radix.add(ratio).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 减法
     * @author tianqi
     * @see
     * @param radix 被减数
     * @param ratio 减数
     * @param scale 四舍五入保留小数
     * @return
     * @throws Exception
     * @datetime 2016年8月9日 下午8:42:53
     * @version 1.0
     */
    public static BigDecimal subtract(BigDecimal radix, BigDecimal ratio, Integer scale){
        //检查是否为null
        checkNull(radix, "被减数");
        checkNull(ratio, "减数");
        BaseUtil.checkNull(scale, "四舍五入保留位");
        //检查是否小于0
//		checkIsPositiveNumber(radix, "被减数");
//		checkIsPositiveNumber(ratio, "减数");
        //检查四舍五入是否在0-16之间
        BaseUtil.checkRange(scale, 0, 16, "四舍五入保留小数位");
        return radix.subtract(ratio).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 乘法
     * @author tianqi
     * @see
     * @param radix 被乘数
     * @param ratio 乘数
     * @param scale 四舍五入保留小数
     * @return
     * @throws Exception
     * @datetime 2016年8月9日 下午8:42:53
     * @version 1.0
     */
    public static BigDecimal multiply(BigDecimal radix, BigDecimal ratio, Integer scale){
        //检查是否为null
        checkNull(radix, "被乘数");
        checkNull(ratio, "乘数");
        BaseUtil.checkNull(scale, "四舍五入保留位");
        //检查是否小于0
//		checkIsPositiveNumber(radix, "被乘数");
//		checkIsPositiveNumber(ratio, "乘数");
        //检查四舍五入是否在0-16之间
        BaseUtil.checkRange(scale, 0, 16, "四舍五入保留小数位");
        return radix.multiply(ratio).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 除法
     * @author tianqi
     * @see
     * @param radix 分子
     * @param ratio 分母
     * @param scale 四舍五入保留小数
     * @return
     * @throws Exception
     * @datetime 2016年8月9日 下午8:42:53
     * @version 1.0
     */
    public static BigDecimal divide(BigDecimal radix, BigDecimal ratio, Integer scale){
        //检查是否为null
        checkNull(radix, "分子");
        checkNull(ratio, "分母");
        BaseUtil.checkNull(scale, "四舍五入保留位");
        //检查是否小于0
//		checkIsPositiveNumber(radix, "分子");
//		checkIsPositiveNumber(ratio, "分母");
        //检查四舍五入是否在0-16之间
        BaseUtil.checkRange(scale, 0, 16, "四舍五入保留小数位");
        return radix.divide(ratio, scale.intValue(), BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 求list中aae019的总和
     * @author tianqi
     * @see
     * @param list CalResultVO中只需要有aae019就行
     * @return
     * @throws Exception
     * @datetime 2016年8月16日 上午10:35:10
     * @version 1.0
     */
    public static BigDecimal calTotal(List list) throws Exception{
        BigDecimal sum = BigDecimal.valueOf(0.00);
        if(ValidateUtil.isEmpty(list)){
            return sum;
        }
        Map map;
        MyMap key = new MyMap();
        for (Iterator iterator = list.iterator(); iterator.hasNext();) {
            map = (Map) iterator.next();
            key.put("aae019", map.get("aae019"));
            sum = add(sum, key.getAsBigDecimal("aae019"), 2);
        }
        return sum;
    }
    /**
     * 求list中某字段金额的总和
     * @author tianqi
     * @see
     * @param list CalResultVO中只需要有传入的字段就行
     * @return
     * @throws Exception
     * @datetime 2016年8月16日 上午10:35:10
     * @version 1.0
     */
    public static BigDecimal calTotal(List list,String zd) throws Exception{
        BigDecimal sum = BigDecimal.valueOf(0.00);
        if(ValidateUtil.isEmpty(list)){
            return sum;
        }
        Map map;
        MyMap key = new MyMap();
        Object o;
        for (Iterator iterator = list.iterator(); iterator.hasNext();) {
            try{
                map = (Map) iterator.next();
            }catch (Exception e){
                o = (Object) iterator.next();
                map = MapUtil.toMap(o);
            }
            key.put(zd, map.get(zd));
            sum = add(sum, key.getAsBigDecimal(zd), 2);
        }
        return sum;
    }

    /**
     * 求list中某字段金额的总和(不对金额做四舍五入)
     * @author tianqi
     * @see
     * @param list 中只需要有传入的字段就行
     * @return
     * @throws Exception
     * @datetime 2016年8月16日 上午10:35:10
     * @version 1.0
     */
    public static BigDecimal calTotalNotRound(List<Map> list,String zd) throws Exception{
        BigDecimal sum = BigDecimal.ZERO;
        if(ValidateUtil.isEmpty(list)){
            return sum;
        }
        MyMap key;
        for (Map map : list) {
            key = new MyMap();
            key.putAll(map);
            if (ValidateUtil.isEmpty(key.get(zd))) {
                continue;
            }
            sum = sum.add(key.getAsBigDecimal(zd));
        }
        return sum;
    }

    public static String positiveIntegerToHanStr(String NumStr) {
        // 输入字符串必须正整数，只允许前导空格(必须右对齐)，不宜有前导零
        String rMBStr = "";
        boolean lastzero = false;
        boolean hasvalue = false; // 亿、万进位前有数值标记
        int len, n;
        len = NumStr.length();
        if( len > 15 ) {
            return "数值过大!";
        }
        for(int i=len-1;i>=0;i--) {
            if( NumStr.charAt(len-i-1)==' ' ) continue;
            n = NumStr.charAt(len-i-1) - '0';
            if( n<0 || n>9 ) return "输入含非数字字符!";
            if( n!=0 ) {
                if( lastzero ) {
                    rMBStr += HanDigiStr[0];  // 若干零后若跟非零值，只显示一个零
                }
                // 除了亿万前的零不带到后面
                //if( !( n==1 && (i%4)==1 && (lastzero || i==len-1) ) )    // 如十进位前有零也不发壹音用此行
                if( !( n==1 && (i%4)==1 && i==len-1 ) ) {    // 十进位处于第一位不发壹音

                    rMBStr += HanDigiStr[n];
                }
                rMBStr += HanDiviStr[i];    // 非零值后加进位，个位为空
                hasvalue = true;                                    // 置万进位前有值标记
            }else{
                if( (i%8)==0 || ((i%8)==4 && hasvalue) ) {  // 亿万之间必须有非零值方显示万

                    rMBStr += HanDiviStr[i];   // “亿”或“万”
                }
            }
            if( i%8==0 ) {
                hasvalue = false ;      // 万进位前有值标记逢亿复位
            }
            lastzero = (n==0) && (i%4!=0);
        }
        if( rMBStr.length()==0 ) {
            return HanDigiStr[0];         // 输入空字符或"0"，返回"零"
        }
        return rMBStr;
    }

    /**
     * BigDecimal转换成大写金额
     * @param val
     * @param flag 是否带￥符号  true带，false不带
     * @return
     */
    public static String numToRMBStr(BigDecimal val, boolean flag){
        return numToRMBStr(val.doubleValue(), flag);
    }

    /**
     * Double类型转换成大写金额
     * @param val
     * @param flag 是否带￥符号  true带，false不带
     * @return
     */
    public static String numToRMBStr(double val, boolean flag){
        String signStr ="" ;
        String TailStr ="";
        long  fraction, integer;
        int jiao,fen;
        if( val<0 ) {
            val = -val;
            signStr = "负";
        }
        if(val > 99999999999999.999 || val <-99999999999999.999 ) {
            return "数值位数过大!";
        }
        // 四舍五入到分
        long temp = Math.round(val*100);
        integer = temp/100;
        fraction = temp%100;
        jiao = (int)fraction/10;
        fen = (int)fraction%10;
        if( jiao==0 && fen==0 ) {
            TailStr = "整";
        }
        else {
            TailStr = HanDigiStr[jiao];
            if( jiao!=0 ) {
                TailStr += "角";
            }
            if( integer==0 && jiao==0 ) {
                TailStr = "";
            }
            if( fen!=0 ) {
                TailStr += HanDigiStr[fen] + "分";
            }
        }
        String strMoney = signStr+positiveIntegerToHanStr(String.valueOf(integer) )+"元"+TailStr;
        return flag ? "￥" + strMoney : strMoney;
    }
}
